'use client'; import { use, useEffect, useRef, useState } from 'react'; import { useDonationHub } from '@/hooks/useDonationHub'; import { fetchApi } from '@/lib/utils/client'; import { GoalProgress } from '@/types/donation'; import './style.scss'; type Props = { params: Promise<{ widgetToken: string }>; searchParams: Promise<{ [key: string]: string|string[]|undefined }>; }; type GoalStyleConfig = { id: number; title: string; style: number; startAmount: number; targetAmount: number; isShowPercent: boolean; barColor: string; barBackgroundColor: string; barHeightPx: number; titleFontSizePx: number; titleFontColor: string; amountFontSizePx: number; amountFontColor: string; titleFontFamily: string|null; amountFontFamily: string|null; isActive: boolean; }; const DEFAULT_STYLE: Omit = { isShowPercent: true, barColor: '#FF6B35', barBackgroundColor: '#333333', barHeightPx: 30, titleFontSizePx: 18, titleFontColor: '#FFFFFF', amountFontSizePx: 14, amountFontColor: '#CCCCCC', titleFontFamily: null, amountFontFamily: null }; export default function GoalPage({ params, searchParams }: Props) { const { widgetToken } = use(params); const sp = use(searchParams); const configID = typeof sp.configID === 'string' ? parseInt(sp.configID, 10) : null; const hubUrl = process.env.NEXT_PUBLIC_API_URL + '/hubs/donation'; const { goalProgress, setGoalProgress } = useDonationHub(widgetToken, hubUrl); const [styleCfg, setStyleCfg] = useState(null); const widgetRef = useRef(null); // 스타일 config 로드 useEffect(() => { const qs = configID ? `?configID=${configID}` : ''; fetchApi(`/api/widget/goal/config/${widgetToken}${qs}`, { silent: true }).then(res => { if (res.data) { setStyleCfg(res.data); } }).catch(() => {}); }, [widgetToken, configID]); // 진행률 데이터 로드 (SignalR broadcast 전 초기 표시) useEffect(() => { const qs = configID ? `?configID=${configID}` : ''; fetchApi(`/api/widget/goal/by-token/${widgetToken}${qs}`, { silent: true }).then(res => { if (res.data) { setGoalProgress(res.data); } }).catch(() => {}); }, [widgetToken, configID, setGoalProgress]); // CSS variables 적용 (goalProgress 없어도 styleCfg 기반으로 빈 진행바 스타일링) useEffect(() => { if (!widgetRef.current) { return; } const el = widgetRef.current; const cfg = styleCfg ?? null; const titleFontSize = cfg?.titleFontSizePx ?? DEFAULT_STYLE.titleFontSizePx; const titleColor = cfg?.titleFontColor ?? DEFAULT_STYLE.titleFontColor; const barHeight = cfg?.barHeightPx ?? DEFAULT_STYLE.barHeightPx; const barBgColor = cfg?.barBackgroundColor ?? DEFAULT_STYLE.barBackgroundColor; const barColor = cfg?.barColor ?? DEFAULT_STYLE.barColor; const amountSize = cfg?.amountFontSizePx ?? DEFAULT_STYLE.amountFontSizePx; const amountColor = cfg?.amountFontColor ?? DEFAULT_STYLE.amountFontColor; const titleFamily = cfg?.titleFontFamily ?? 'inherit'; const amountFamily = cfg?.amountFontFamily ?? 'inherit'; const fillPercent = goalProgress ? Math.min(goalProgress.percent, 100) : 0; el.style.setProperty('--goal-title-font-size', `${titleFontSize}px`); el.style.setProperty('--goal-title-color', titleColor); el.style.setProperty('--goal-title-font-family', titleFamily); el.style.setProperty('--goal-bar-height', `${barHeight}px`); el.style.setProperty('--goal-bar-bg-color', barBgColor); el.style.setProperty('--goal-bar-color', barColor); el.style.setProperty('--goal-bar-fill-width', `${fillPercent}%`); el.style.setProperty('--goal-amount-font-size', `${amountSize}px`); el.style.setProperty('--goal-amount-color', amountColor); el.style.setProperty('--goal-amount-font-family', amountFamily); }, [goalProgress, styleCfg]); // goalProgress 없어도 styleCfg 기반으로 빈 진행바 표시 (위젯 즉시 노출) const display = goalProgress ?? { title: styleCfg?.title ?? '후원 목표', startAmount: styleCfg?.startAmount ?? 0, targetAmount: styleCfg?.targetAmount ?? 0, currentAmount: styleCfg?.startAmount ?? 0, percent: 0 }; const { title, currentAmount, targetAmount, percent } = display; const isShowPercent = styleCfg?.isShowPercent ?? DEFAULT_STYLE.isShowPercent; return (
{title}
{currentAmount.toLocaleString()}원 / {targetAmount.toLocaleString()}원 {isShowPercent && ` (${percent}%)`}
); }